import { MessageTip } from '@app-cad/common/utils/MessageTip';
import { ctx2 } from '@app-cad/module';
import { Object3D, Vector2tuple, Vector3 } from 'three-legacy';
import { ConstraintsType } from '../../CmdBase';
import { DimObject } from '../cmd-render-pick/type';
import { DimLinear } from './DimLinear';
import { DimRadiusTool } from './DimRadiusTool';
import { AnnotationType, DimensionLinear, LocalOriginType } from './types';

// should be imported later than `DimLinear` to avoid `access 'DimBase' before initialization`
import { DimDiameterTool } from './DimDiameter';

type DimInfo = {
  id?: string;
  objects?: string[];
  value?: number;
  points?: Vector2tuple[];
  master?: boolean;
  type?: AnnotationType;
};
const ellipseDimType = [
  AnnotationType.MajorRadiusAnno,
  AnnotationType.MinorRadiusAnno,
];

export class Dimension {
  private assisGroup = ctx2.cadScene.assisGroup;
  public dimType: 'preview' | 'create' = 'preview';
  private dimension: DimObject;
  static instance: Dimension;
  private isFocus: boolean = false;
  private dimInfo: DimInfo;
  private isArc: boolean = false;
  private points: Vector2tuple[] = [];

  static getInstance(): Dimension {
    if (!this.instance) {
      this.instance = new Dimension();
    }
    return this.instance;
  }

  get dimObj() {
    return this.dimension;
  }

  setAssisGroup(value: Object3D) {
    this.assisGroup = value;
  }

  getyL(direction: -1 | 1, L: number) {
    return (direction * L) / ctx2.getCameraZoom();
  }

  getDimInfo = () => this.dimInfo;
  destroy = () => {
    this.dimType = 'preview';
    this.dimension?.destroy();
    this.dimension = null;
    this.isFocus = false;
    this.points = [];
  };

  reset() {
    this?.destroy();
    this.dimInfo = null;
  }

  // focus
  determineDim = (minVal?: number) => {
    this.dimension?.blur();
    const val = this.dimension?.getVal();
    if (
      (minVal != null && val + 1e-5 < minVal) ||
      !this.dimension ||
      this.dimension.getVal() === 0
    ) {
      if (minVal != null) {
        MessageTip.error('请输入合适的尺寸');
      }
      this.dimension?.resetDimLabel(minVal || 1);
      this.focus();
      return false;
    }
    this.dimInfo = {
      points: this.dimension.getPosLv(),
      value: val,
      type: this.dimension.userData.type,
      master: true,
    };
    this.dimType = 'create';
    this.dimension.setNormalColor();
    return true;
  };

  dimLine(
    ps: [Vector3, Vector3],
    sing: -1 | 1 = 1,
    isFocus: boolean = true,
    localOriginType?: LocalOriginType,
  ) {
    const [startPoint, endPoint] = ps;
    if (startPoint.equals(endPoint)) {
      return;
    }
    const vertices = ps.map((point) => [point.x, point.y]) as Vector2tuple[];
    this.points = vertices;
    const xL = startPoint.distanceTo(endPoint) / 2;
    const yL = this.getyL(sing, 100);
    const params: DimensionLinear = {
      lineVertices: [vertices],
      relPos: localOriginType === 'center' ? [[0, yL]] : [[xL, yL]],
    };
    if (!this.dimension) {
      this.dimension = new DimLinear({ ...params, localOriginType });
      this.dimension.addTo(this.assisGroup);
      setTimeout(() => this.preview(isFocus), 50);
    } else {
      this.dimension.update(params);
      if (this.isFocus && this.dimType === 'preview') {
        this.focus();
      }
    }
  }

  dimDiameter = (center: Vector3, radius: number, isFocus = true) => {
    if (!this.dimension) {
      this.dimension = new DimDiameterTool({ center, radius });
      this.preview(isFocus);
      this.dimension.addTo(this.assisGroup);
    } else {
      this.dimension.update({ radius });
      (this.dimension as DimDiameterTool).defaultMouseXY();
      if (this.isFocus && this.dimType === 'preview') {
        this.focus();
      }
    }
  };
  dimRadius = (center, ps, radius, isFocus = true) => {
    const [point1, point2] = ps;
    const param = { center, point1, point2, radius };
    if (!this.dimension) {
      this.dimension = new DimRadiusTool(param);
      this.preview(isFocus);
      this.dimension.addTo(this.assisGroup);
      this.isArc = true;
    } else {
      this.dimension.update(param);
      if (this.isFocus && this.dimType === 'preview') {
        this.focus();
      }
    }
    (this.dimension as DimRadiusTool).defaultMouseXY(true);
  };

  preview(isFocus = true) {
    this.dimension?.labelFoucs();
    this.dimension?.preview();
    if (isFocus) {
      this.focus();
    }
  }

  focus() {
    this.isFocus = true;
    setTimeout(() => {
      this.dimension?.focus();
    }, 120);
  }

  addDimension(curveIds = [], annotationType?: AnnotationType, multiple = 1) {
    if (!this.dimInfo || this.dimType === 'preview') {
      return null;
    }
    if (this.isArc) {
      this.dimInfo.points = this.dimension.getPosLv();
    } else if (this.dimInfo.type === AnnotationType.RadiusAnno) {
      const val = this.getyL(1, 25) + this.dimInfo.value;
      this.dimInfo.points = [
        [val, val],
        [1, 0],
      ];
    } else if (this.dimInfo.type === AnnotationType.DistanceAnno) {
      this.dimInfo.points[0][0] = this.dimInfo.value / 2;
    }
    if (ellipseDimType.includes(annotationType)) {
      this.dimInfo.points[0][0] = 0;
    }
    let curve = curveIds?.[0] || '';
    let target = curveIds?.[1] || curveIds?.[0] || '';
    if (this.points.length === 2 && !ellipseDimType.includes(annotationType)) {
      curve = this.points[0];
      target = this.points[1];
    }
    const res: ConstraintsType<AnnotationType> = {
      curve,
      target,
      type: annotationType || this.dimInfo.type,
      value: this.dimInfo.value / multiple,
      points: this.dimInfo.points,
    };
    this.dimInfo = null;
    this.isArc = false;
    return res;
  }
}

export const dimension = () => {
  return Dimension.getInstance();
};
